═══ 1. Product Information ═══ Author: Dev Banerjee (C) Copyright International Business Machines Corporation 1994. All rights reserved ═══ 2. What is APMT? ═══ APMT is a program package that provides a set of REXX functions. These functions can be used to write REXX programs which can drive any PM application running in the same PC or in a different PC. Some of the functions query the state of the PM window controls, while others change them. This allows the REXX program (hereinafter called APMT client) to act in place of a human operator. An APMT client program does not require any special environment to run under. It can be invoked from any OS/2 command line or equivalent. ═══ 2.1. Uses ═══ Following are some uses of APMT. o As scripts to run automated test programs for PM applications. This is the main intended use of the tool. APMT is intended for testing a product based on its specifications, in a way that automated testcases can be written without requiring a product driver to be available first. APMT does not have screen save/compare, or testcase record/playback features. o As a driver for programs to be executed on remote machines. Another possible use of APMT is to start a program in a remote machine. If that program is a PM application then it can be monitored and driven using APMT functions. o As an interface between a REXX program and standard OS/2 Workplace Shell controls. APMT can be used to develop simple, but yet effective, programs to manipulate Workplace objects which can help automate routine tasks. ═══ 3. Installation and use ═══ ═══ 3.1. Prerequisites ═══ APMT runs on OS/2 2.x and requires REXX to be installed. If the target application is on a remote machine, both machines need to be running OS/2 2.x and be LAN connected. ═══ 3.2. How to Install. ═══ Installation consists of the following. Note that the files need to be in the directories indicated, regardless of whether they are in the current directory, in order for APMT to run reliably. move APMTEXT.DLL to a directory in the LIBPATH move APMTDLL.DLL to a directory in the LIBPATH move APMOCRW.DLL to a directory in the LIBPATH move APMNP.DLL to a directory in the LIBPATH move APMNETB.DLL to a directory in the LIBPATH * needed only for remote operation move APMT.EXE to a directory in the PATH move APMNETBL.EXE to a directory in the PATH * needed only for remote operation move IBMDABB.CP to a directory in the DPATH Note for users of Distributed Applications/2. APMT is an application that uses Distributed Applications/2. The file IBMDABB.CP contains the connection profiles. If you are a user of other Distributed Applications/2 applications, you may want to combine the IBMDABB.CP files for the different applications. ═══ 4. Limitations ═══ APMT can only operate on controls using standard PM classes. Therefore if an application uses non standard classes then APMT cannot be used for these controls. A limited support for non standard controls is provided via the MOUSE functions. The set of functions provided in the current package allow you to perform many interesting tasks. However, it easy to imagine additional functions that could be added to the package. Please provide suggestions/requirements to the author. ═══ 4.1. Considerations for timing related problems ═══ Since APMT client will tend to drive the application much faster than a human operator, it will be necessary at times to pause so that the selected actions are not executed too early. For example, after pressing a button to cancel a modal dialog box, it may be necessary to wait before directing any input to the parent window to allow time for the dialog box to be dismissed. Often, it is possible to set up a simple Rexx loop to wait for the desired condition before proceeding further. The WAIT and SET_TIMEOUT functions, and the retry parameters to the SELECT_WINDOW, and SELECT_DIALOG_WINDOW are some of the means to achieve the delay. Also, if the application displays the "wait" pointer, APMT automatically waits for it to be cleared before servicing a function. ═══ 5. APMT client Program Structure ═══ The APMT client is simply a REXX program using APMT functions and any other REXX functions and features. The following is a simple example of the structure of an APMT client program. o Startup instructions as follows: /******************************************************************/ /* Part 1: common prefix */ /******************************************************************/ Trace off call APMT_INIT o Main APMT client instructions, for example: /******************************************************************/ /* Part 2: Program specific instructions */ /******************************************************************/ rc = SELECT_WINDOW("Icon Editor","10") ... o Terminating instructions, as follows: /******************************************************************/ /* Part 3: common suffix */ /******************************************************************/ APMT_CLOSE: rc = END_SESSION(); exit DropFUNC: /* function normally not needed */ call APMTDropFuncs; call rxfuncdrop(APMTDropFuncs) return APMT_INIT: call rxfuncadd 'APMTLoadFuncs', 'apmtext', 'APMTLoadFuncs' /* entry points from the DLL */ call APMTLoadFuncs; rc = INIT_SESSION(); if rc \= 0 then do say apmtmsg exit end signal on error name APMT_CLOSE if apmtver.client \= apmtver.server then say 'WARNING: Version mismatch. Client ='apmtver.client 'Server ='apmtver.server else say 'Running APMT version 'apmtver.client signal on halt name APMT_CLOSE return ═══ 5.1. General syntax rules ═══ o All APMT client calls are of the same format: rc = APMT_CALL(parm1, .. ,parmN) o If a parm is optional, it is indicated in the description of the function. If a required parm is missing REXX will return with 'INCORRECT CALL TO ROUTINE' message. o Some functions return data to the APMT client. o Three types of data are returned: Rexx single variables Rexx stem variables OS/2 files The type of data to be returned is indicated in the function description. For these functions, the name of the variable, or file, is always the last parameter of the function. It is safer to provide this name in quotes. The value of a stem variable is as updated as follows. If "xxx" is the variable name passed, then on return, xxx.0 contains the number of additional stems. xxx.1, .. xxx.(xxx.0) contain the rest of the stem values. Other stems of xxx are unchanged. The value of the variable is set to the null string if the call was unsuccessful with a return code greater than 0. For stem variables, the .0 stem is set to null and any other stems are unchanged. o Many of the functions require the text of the window control as parameter to identify the window. Except for SELECT_WINDOW and SELECT_DIALOGWINDOW, the text should be exact. The match is case sensitive, and blanks are not ignored. o Some functions require the sequence number of the window control as it appear in the selected window to identify it. o Controls are assigned sequence numbers according to their appearance in the selected window, from top to bottom and left to right. ═══ 5.2. Return codes and error messages ═══ In the description of a APMT function, only the return codes which are unique for that function are explained. The common return codes apply to all functions and are not repeated. Return code 0 indicates success. However, for some functions, this simply means that the appropriate message was sent to the application. The results of that message is not indicated in this return code. Return code 4 indicates that APMT could not locate the specified control window. Return codes between 5 and 7 indicate a call specific error condition and are described in the function description. Return code 8 indicates an incorrect environment for the call. For example, a PUSHBUTTON_CLICK call without a selected window will result in this code. This code will also occur if the target application has been terminated or some other condition has made the selected window invalid. Return code 12 indicates that the session is not open. Return codes 20 or greater indicate a possible bug in APMT. Please report these to the author. Syntax and environment setup errors are intercepted by REXX and result in such messages as 'ROUTINE NOT FOUND' and 'INCORRECT CALL TO ROUTINE' APMT sets a rexx variable, APMTMSG, after each function call. If the return code from the function is 0, this variable is set to null. If the return code is non-zero, this variable contains a message indicating the cause. ═══ 6. The APMT Functions ═══ This section describes all the APMT client functions. As the tool evolves, more functions will be added or existing functions will be enhanced. ═══ 6.1. INIT_SESSION ═══ This function establishes a APMT session. Syntax rc = INIT_SESSION([app_location]) where app_location is RMTNETnn, and nn = {01, 02, .. 20} Examples rc = INIT_SESSION() rc = INIT_SESSION("RMTNET01") Description This function must be the first APMT call before any significant function can be called. The parameter is optional and identifies where the target application resides. If it is missing, then it is assumed to be in the same PC. If it is on a different PC, then the following has to be done prior to this call: APMT installed on the remote machine. The two machines must be on the same LAN. Invoke "APMNETBL RMTNETnn" from any OS/2 command line on the remote machine. This function will start an OS/2 session which has no screen i/o. This session should be kept running as long as an APMT session to that machine is needed. If you get an error when you run APMNETBL, you also get a NETBIOS return code which indicates the error. Common causes of failure are name RMTNETnn already in use, no NETBIOS sessions available, and network not accessed. Note that APMNETBL essentially identifies the remote machine to the LAN, and therefore two distinct invocations cannot use the same nn value simultaneously. The invocation parameter for INIT_SESSION should match the parameter to APMNETBL. Note on remote connections. It is possible to have the target application on an APPC-connected OS/2 machine also. Contact the author if you have any needs/interest in this. Return Codes o 0 - connection successful o 8 - connection unsuccessful o 12 - too many sessions Notes If the target is on a different machine, this function may take somewhat longer to complete, perhaps as much as 15 seconds. The result will be that the REXX program will appear slow to start. After the connection is complete, there should not be any additional delay, however, for subsequent actions. The session is closed by the END_SESSION call. When a session is opened, APMT updates two stem variables APMTVER.CLIENT, and APMTVER.SERVER. These are only needed to ensure that different part of APMT are at the same version level. The client here is the APMT client program, and the server is the part of APMT which drives the target application. It is important that the versions match. APMT will support up to 20 sessions at the same time. After a session is opened, all APMT calls are directed to this session, until another session is opened with INIT_SESSION, or SELECT_SESSION is called to switch to another open session. ═══ 6.2. END_SESSION ═══ This function ends the currently selected APMT session. Syntax rc = END_SESSION() Examples rc = INIT_SESSION('RMTNET01') rc = INIT_SESSION('RMTNET02') rc = SELECT_SESSION('RMTNET01') ... rc = SELECT_SESSION('RMTNET02') ... rc = SELECT_SESSION('RMTNET01') ... rc = END_SESSION() /* closes RMTNET01 */ rc = SELECT_SESSION('RMTNET02') rc = END_SESSION() /* closes RMTNET02 */ Description This function will close the currently selected session, and on return no session remains selected, even if there are open sessions. To select a open session at this time, use SELECT_SESSION. It is recommended that each open session be closed before exiting the APMT client program. Return Codes o 0 - always. ═══ 6.3. SELECT_SESSION ═══ This function selects an open session for subsequent APMT functions. Syntax rc = SELECT_SESSION(app_location) where app_location is RMTNETnn, and nn = {01, 02, .. 20} Examples rc = INIT_SESSION('RMTNET01') rc = INIT_SESSION('RMTNET02') rc = SELECT_SESSION('RMTNET01') ... rc = SELECT_SESSION('RMTNET02') ... rc = SELECT_SESSION('RMTNET01') ... rc = END_SESSION() /* closes RMTNET01 */ rc = SELECT_SESSION('RMTNET02') rc = END_SESSION() /* closes RMTNET02 */ Description This function will select the session specified in the parameter, provided it is open. Return Codes o 8 - specified session is not open ═══ 6.4. START_PROGRAM ═══ This function may be used to start an OS/2 program. Syntax rc = START_PROGRAM(pgm_name[,parm_string][,working_dir]) Examples rc = START_PROGRAM("MYAPP.EXE") rc = START_PROGRAM("MYAPP.EXE","myparm1, myparm2") rc = START_PROGRAM("MYAPP.EXE","","Y:\TEST") Description This function will start an OS/2 session with the specified program name and pass the specified parameter string to the program. If the working_dir parameter is specified, the program will be started after changing the current directory and disk to the specified working directory. It may be necessary to do this for example if the program depends on DLLs to be located in the current directory. Return Codes o 0 - The program was started o 5 - Invalid working directory specified. o 6 - The program startup failed. Notes For the purposes of APMT, the program is expected to be a PM application. However, this function can be used to start non-PM applications also. Even though the application is started successfully, its application window may not be ready, and it may be necessary to wait for a subsequent SELECT_WINDOW to be successful. Either the WAIT function or the second parameter of SELECT_WINDOW may be used for this purpose. ═══ 6.5. SELECT_WINDOW ═══ This function selects a main application window as the target for subsequent actions. Syntax rc = SELECT_WINDOW(window_title [,number_of_attempts] [,seq_number]) Examples rc = SELECT_WINDOW("Icon Editor") rc = SELECT_WINDOW("Icon Editor", "15") rc = SELECT_WINDOW("I*Editor", "15") /* using wild card character * */ /* List all the windows on the desktop */ n = 1 do while SELECT_WINDOW("*",1,n) = 0 rc = QUERY_TITLE("title") say 'WINDOW NO. 'n ': 'title n = n+1 end Description This function locates an application window with the given title and selects it for subsequent actions. A window must be selected before any other functions can be directed to it. Any previously selected window will be de-selected if this call is successful. The specified title can contain wild card characters * and ?. The second parameter is an optional timeout value. If the window cannot be located then the second parameter indicates how many attempts will be made. APMT waits for 100 milliseconds (1/10 sec) before trying again. So, in the second example above, at least 1.5 seconds will elapse before the function will fail. This value defaults to, and must be at least, 1. If any other value is specified, 1 is used. The third parameter is optional, and defaults to 1. If there are more than one window which match the title specification in the first parameter, then seq_number can be used to select the desired window. 1 means the topmost window on the desktop. Notes If the specified window is not found, then the currently selected window will remain selected. A window on the desktop whose title matches the given window_title, after taking any wild card characters into account, will be selected. To specify prefix only, the window_title must end in a *. The match is case sensitive. If there is more than one window which matches the title, then APMT will select the topmost window. There is no relationship between the selected window and the application started by START_PROGRAM. Specifying "*" as the title will select the active window on the desktop. Only one window can be selected at a time. APMT will place the mouse pointer at the center of the selected window if this call is successful. ═══ 6.6. SELECT_WINDOW_BY_HANDLE ═══ This function selects a window whose handle is specified as the parameter. Syntax rc = SELECT_WINDOW_BY_HANDLE(window_handle) Examples /* Save window handle, and later select it by specifying handle */ rc = SELECT_WINDOW("CMD.EXE") rc = QUERY_WINDOW_HANDLE("savehandle") ... rc = SELECT_WINDOW_BY_HANDLE(savehandle) Description This function is similar to SELECT_WINDOW, but it uses the window handle as parameter. To get the window handle, use the QUERY_WINDOW_HANDLE function. Notes. o This function, in conjunction with QUERY_WINDOW_HANDLE, can be used to save and restore window selections. This is helpful when the window title of the selected window changes, or there are multiple windows with the same title, and you need to select the particular window that you had selected before. o APMT will not place the mouse pointer at the center of the selected window. ═══ 6.7. SELECT_DIALOGWINDOW ═══ This function selects a window on the desktop, which is a dialog box for the currently selected window, for subsequent actions. Syntax rc = SELECT_DIALOGWINDOW(window_title [,number_of_attempts]) Examples rc = SELECT_DIALOGWINDOW("Device List") rc = SELECT_DIALOGWINDOW("Device List", "15") rc = SELECT_DIALOGWINDOW(" ") Description This function is similar to SELECT_WINDOW, and it selects a window which belongs to the same process to which the currently selected window belongs. Notes. o This call deselects the previously selected window if the dialog window is successful, otherwise the previously selected window remains selected. o The matching he desktop whose title matches the given window_title, after taking any wild card characters into account, will be selected. To specify a prefix only, the window_title must end in a *. The title may be "*". In this case the topmost window on the desktop is selected (running in the application's process), regardless of its title. This function is useful if the target application displays message boxes which have blank titles or the title is unknown. To return to the previous window, use SELECT_WINDOW again. The second parameter is optional and has the same meaning as for SELECT_WINDOW. o APMT will place the mouse pointer at the center of the selected window if this call is successful. ═══ 6.8. SELECT_SUBWINDOW ═══ This function selects a frame window which is a child of the currently selected window, and returns its title. Syntax rc = SELECT_SUBWINDOW([seq_number,] variable_name) Examples rc = SELECT_WINDOW(Name) do i = 1 to 5 rc = SELECT_SUBWINDOW(i,"title") if rc = 0 then say "subwindow "i " :" title rc = SELECT_WINDOW(Name) end Description This function is provided to list the titles of sub windows in an IPF help window, but other uses are possible. This changed the current window selection. The first parameter is optional and indicates the sequence number of the subwindow to be selected. Default is 1. APMT will NOT place the mouse pointer at the center of the sub window. ═══ 6.9. SET_FOCUS ═══ This function makes the selected window the active window. Syntax rc = SET_FOCUS() Examples rc = SET_FOCUS() Description This function will make the selected window active. Return Codes o 5 - Current window could not be made active. Notes There is no need to use this function for subsequent APMT functions. Use this only if you have a special need. ═══ 6.10. SET_TIMEOUT ═══ This function sets the default time out for selected APMT functions. Syntax rc = SET_TIMEOUT(number_of_attempts) Examples rc = SET_TIMEOUT(200) Description The parameter is a timeout value. If certain APMT functions, identified below, will not work until the application is ready, then APMT will make number_of_attempts before giving up. APMT waits for 100 milliseconds (1/10 sec) before trying again. This value defaults to, and must be at least, 1. If any other value is specified, 1 is used. This is a persistent setting, until changed by another SET_TIMEOUT call. This function is intended to help the APMT client program synchronize with the target PM application. For example, if MENU_SELECT is invoked, and the timeout is set to 200 then APMT will make up to 200 attempts, if it finds the menu item disabled, before giving up. This gives approximately 20 seconds to the application to enable the menu item. The following functions and events are affected by the timeout value. o POPUPMENU_QUERY_ALL - Wait for popup menu to appear o POPUPMENU_SELECT - Wait for popup menu to appear o POPUPMENU_QUERY_STATE - Wait for popup menu to appear o SYSMENU_SELECT - Wait for menu item to be enabled o MENU_SELECT - Wait for menu item to be enabled Notes As needs are identified, more functions and events will be added to this list. Return Codes o 0 - Always. ═══ 6.11. QUERY_WINDOW_HANDLE ═══ Query the window handle of the selected window. Syntax rc = QUERY_WINDOW_HANDLE(variable_name) Examples /* Get the handle of the selected window */ rc = QUERY_TITLE("handle") say "handle is " handle Description This call returns a variable which contains the handle of the currently selected window. This handle is intended to be used with a subsequent SELECT_WINDOW_BY_HANDLE function call. ═══ 6.12. QUERY_PREV_RESPONSETIME ═══ Query the response time for the previous action, in milliseconds. Syntax rc = QUERY_PREV_RESPONSETIME(variable_name) Examples /* Press the PUSHBUTTON "HELP" */ rc = PUSHBUTTON_CLICK("HELP") /* Wait for a the help window to appear */ rc = SELECT_WINDOW("Help for*",100) /* Display the response time */ if rc = 0 then do rc = QUERY_PREV_RESPONSETIME("resp") say 'The help window took 'resp 'milliseconds to appear' end Description For certain functions, APMT automatically notes the time the command was executed. These commands are: PUSHBUTTON_CLICK, START_PROGRAM, MENU_SELECT, and KEYBOARD with "ENTER" and "ESC" virtual keys. If these functions are immediately followed by SELECT_WINDOW, then APMT calculates the time elapsed between the previous action and the window's appearance on the desktop. If the next function is QUERY_PREV_RESPONSETIME, then this time elapsed is returned in the Rexx variable. If the conditions are not met, this function will return with rc=5. Return Codes o 5 - No response time to report. ═══ 6.13. QUERY_TITLE ═══ Query the title of the selected window Syntax rc = QUERY_TITLE(variable_name) Examples /* Get the full title of the selected window */ rc = QUERY_TITLE("title") say "title is " title Description This call returns a variable which contains the full title of the currently selected window. This may be different from the title text passed to the previous SELECT_WINDOW or SELECT_DIALOGWINDOW, since in those functions ═══ 6.14. SAVE_IMAGE ═══ Save the currently selected window's image in a bitmap file Syntax rc = SAVE_IMAGE(file_name) Examples /* Get the BITMAP image of the selected window */ rc = SAVE_IMAGE("current.bmp") Description This call saves the bitmap image of the currently selected window, as a bitmap file in the machine. The parameter must be a valid file name. Notes The bitmap is in os/2 1.2 format currently. This may change in the future. To view the bitmap, another suitable tool is needed (not provided with APMT). Return Codes o 5 - The bitmap could not be created. o 6 - The specified file could not be opened o 7 - The specified file could not be written to ═══ 6.15. WAIT ═══ This function causes a delay for the specified duration. Syntax rc = WAIT(number_of_milliseconds) Examples rc = WAIT("15000") Description This function effectively pauses the REXX program. The number_of_milliseconds is at least 1. Return Codes o 0 - always Notes The timing is approximate, the real wait time will be slightly higher. This function simply issues a DosSleep call on behalf of the REXX program. ═══ 6.16. CHECKBOX_CLICK ═══ This function causes the specified CHECKBOX to be clicked. Syntax rc = CHECKBOX_CLICK(id[,seq_number]) where id is the text of the checkbox, and seq_number is the sequence number of the checkbox with the same id. Examples /* Click the second checkbox labelled 'D' */ rc = CHECKBOX_CLICK("D",2) /* Click the first checkbox labelled 'red' */ rc = CHECKBOX_CLICK("red") Description As a result of this call the specified checkbox may go from checked to unchecked state or vice versa depending on its previous state. In conjunction with the CHECKBOX_QUERY_STATE function, this function can be used to set the desired state. The second parameter is optional and defaults to 1. It is needed only if the window has more than one checkbox with the same id. ═══ 6.17. CHECKBOX_QUERY_ALL ═══ Return the information on all checkboxes. Syntax rc = CHECKBOX_QUERY_ALL(stem_variable_name) Examples /* How many checkboxes? */ rc = CHECKBOX_QUERY_ALL( "cbox") say "number of checkboxes: " cbox.0 do i = 1 to cbox.0 say 'checkbox no 'i 'is: 'cbox.i end Description This function returns a stem variable which contains the information on all the checkboxes in the selected window. ═══ 6.18. CHECKBOX_QUERY_STATE ═══ This function returns the checked, and enabled status of the specified checkbox. Syntax rc = CHECKBOX_QUERY_STATE(id, stem_variable_name) where id is the text identifying the checkbox. Examples /* Make sure that the drive D is selected, and C is not selected */ rc = CHECKBOX_QUERY_STATE("D","state") if rc = 0 then do say 'Check status: 'state.1 say 'Enable status: 'state.2 end if (state.1 = "UNCHECKED") then rc = CHECKBOX_CLICK("D") rc = CHECKBOX_QUERY_STATE("C","state") if (state.1 = "CHECKED") then rc = CHECKBOX_CLICK("C") Description This function returns updates the stem variable specified in the last parameter as follows. The .1 stem indicates the checked status and is either "CHECKED" or "UNCHECKED". The .2 stem indicates the enabled status and is either "ENABLED" or "DISABLED". ═══ 6.19. COMBOBOX_QUERY_ALL_TEXT ═══ Query the text of each item in a combobox. Syntax rc = COMBOBOX_QUERY_ALL_TEXT(seq_number, stem_variable_name) where seq_number is the sequence number identifying the combobox. Examples /* Look at the items of the second combobox */ rc = COMBOBOX_QUERY_ALL_TEXT("2", "txt") say txt.0 "items found" do i = 1 to txt.0 say txt.i end Description This call returns a stem variable. The .0 stem contains the number of items in the combobox. The .i stem contains the text of the i-th item. ═══ 6.20. COMBOBOX_QUERY_TEXT ═══ Query the text of the entryfield in a combobox. Syntax rc = COMBOBOX_QUERY_TEXT(seq_number, variable_name) where seq_number is the sequence number identifying the combobox. Examples /* Look at the entryfield of the second combobox */ rc = COMBOBOX_QUERY_TEXT("2", "txt") say txt Description This call returns a variable whose value is the entryfield of the combobox. ═══ 6.21. COMBOBOX_SELECTITEM ═══ Select the specified item in a combobox. Syntax rc = COMBOBOX_QUERY_ALL_TEXT(seq_number, item_number) where seq_number is the sequence number identifying the combobox, and item_number identifies the item. Examples /* Select the third item of the second combobox */ rc = COMBOBOX_SELECTITEM("2", "3") Description This call selects the specified item of the combobox. Return Codes o 6 - Selection failed. Probably item number is out of range. ═══ 6.22. COMBOBOX_SET_TEXT ═══ Set the text of the entryfield in a combobox. Syntax rc = COMBOBOX_SET_TEXT( seq_number, text) where seq_number is the sequence number identifying the combobox, and text is what it should be set to. Examples /* Set the value of the second combobox */ rc = COMBOBOX_SET_TEXT("2", "D:") Description This function sets the combobox text as specified if the text matches one of the items. If the specified text matches one of the items in the combobox, that item is selected. The specified text has to match only the prefix of an item. Notes This is a 'convenience' function. A better way is to enter the data is to use ENTRYFIELD_SET_FOCUS, followed by KEYBOARD. Return Codes o 5 - The text does not match one of the items. ═══ 6.23. CONTAINER_DESELECTITEM ═══ Deselect an item in the specified container. Syntax rc = CONTAINER_DESELECTITEM(seq_number, object_identifier) where seq_number is the sequence number identifying the container, and object_identifier is either the text string identifying the object or #n with n = the object number Examples rc = CONTAINER_DESELECTITEM(1, "Main") rc = CONTAINER_DESELECTITEM(1, "#5") Description This function deselects an object whose text has a prefix matching object_text. The match is case sensitive. The object may also be identified with "#" followed by its sequence number in the container. Return Codes o 5 - No matching object found. ═══ 6.24. CONTAINER_QUERY_COUNT ═══ Return the number of objects in the specified container. Syntax rc = CONTAINER_QUERY_COUNT(seq_number, variable_name) where seq_number is the sequence number identifying the CONTAINER. Examples /* How many objects? */ rc = CONTAINER_QUERY_COUNT("1", "count") say "number of objects in the container: " count Description This function returns a variable which contains the number of objects in the container identified by the sequence number in the selected window. ═══ 6.25. CONTAINER_SELECTITEM ═══ Select (high light) an item in the specified container. Syntax rc = CONTAINER_SELECTITEM(seq_number, object_identifier) where seq_number is the sequence number identifying the container, and object_identifier is either the text string identifying the object or #n with n = the object number Examples /* Open the object labeled "Main" */ rc = CONTAINER_SELECTITEM(1, "Main") rc = KEYBOARD("ENTER") /* Select the 5th item on the desktop */ rc = SELECT_WINDOW("Desktop") rc = CONTAINER_SELECTITEM(1, "#5") Description This function only highlights an object whose text has a prefix matching object_text. The match is case sensitive. The object may also be identified with "#" followed by its sequence number in the container. Return Codes o 5 - No matching object found. Notes The first parameter is expected to be 1 as most windows have only one container. Other objects which were previously selected, remain selected. By using KEYBOARD("\","C"), all the objects may be de-selected. ═══ 6.26. ENTRYFIELD_HOW_MANY ═══ Return the number of entryfields. Syntax rc = ENTRYFIELD_HOW_MANY( variable_name) Examples /* How many entryfields? */ rc = ENTRYFIELD_HOW_MANY( "count") say "number of entryfields: " count Description This function returns a variable which contains the number of entryfields in the selected window. ═══ 6.27. ENTRYFIELD_QUERY_TEXT ═══ Query the text of an entry field. Syntax rc = ENTRYFIELD_QUERY_TEXT( seq_number, variable_name) where seq_number is the sequence number identifying the entryfield. Examples /* Look at the items of the first entryfield */ rc = ENTRYFIELD_QUERY_TEXT("1", "txt") say txt Description This function returns a variable which contains the text of the specified entryfield. ═══ 6.28. ENTRYFIELD_SET_FOCUS ═══ This function sets the cursor on the specified entry field. Syntax rc = ENTRYFIELD_SET_FOCUS(seq_number) Examples /* Enter the string text into the second entryfield */ rc = ENTRYFIELD_SET_FOCUS(2) rc = KEYBOARD("HOME"); rc = KEYBOARD("DELETE","C"); text = 'PASSWORD' rc = KEYBOARD(text) Description This function is used to set the input focus into a entry field. Notes For the purposes of this function, the entryfield part of a combobox is treated like any other entryfield. For example if the window has entryfield 1, combobox, entryfield 2, this function will treat the combobox as entryfield 2, and entryfield 2 as entryfield 3. ═══ 6.29. ENTRYFIELD_SET_TEXT ═══ Set the text of an entry field. Syntax rc = ENTRYFIELD_SET_TEXT( seq_number, text) where seq_number is the sequence number identifying the entryfield, and text is what it should be set to. Examples /* Set the value of the first entryfield */ rc = ENTRYFIELD_SET_TEXT("1", "MYAPP.EXE") Description This function sets the entryfield as specified. APMT will not update the field if it is not input enabled. Notes This is a 'convenience' function. It may be misused by entering data in a way not available to the user. For example, if this is used to set the text in a non-display field (as for passwords), the password will be visible. A better way is to use ENTRYFIELD_SET_FOCUS, followed by KEYBOARD calls. Return Codes o 6 - The field is not enabled. ═══ 6.30. KEYBOARD ═══ Simulate a keyboard event. Syntax rc = KEYBOARD( key [,shift_state]) Examples rc = KEYBOARD("O","A") /* simulate ALT+O */ rc = KEYBOARD("P","SA") /* simulate ALT+SHIFT+P */ rc = KEYBOARD("4","C") /* simulate CTRL+4 */ rc = KEYBOARD("F4","A") /* simulate ALT+F4 */ rc = KEYBOARD("ENTER") /* simulate ENTER */ rc = KEYBOARD("ENTE") /* simulate E,N,T,E */ rc = KEYBOARD("F12") /* simulate F12 */ rc = KEYBOARD("F123") /* simulate F,1,2,3 */ Description The first parameter identifies either a virtual key or a character key. If the first parameter is not a recognized virtual key, all its characters will be played successively. However, strings that are longer than 30 may be truncated when played by APMT The second parameter is optional and is a string consisting of one or more of the characters 'A', 'C', or 'S' to indicate ALT, CTRL, and SHIFT states respectively. The order of these characters is irrelevant, and duplicates and other characters are ignored. The following virtual keys are recognized. These are case sensitive. o BACKSPACE o BACKTAB o DELETE o DOWN o END o ENTER o ESC o F1 o F2 o F3 o F4 o F5 o F6 o F7 o F8 o F9 o F10 o F11 o F12 o HOME o INSERT o LEFT o NEWLINE o PAGEDOWN o PAGEUP o RIGHT o SPACE o TAB o UP Return Codes o 5 - Current window could not be made active. Notes This function determines the keystroke message to be played to the application. It is not guaranteed that this will have the same effect as a user's keyboard input, depending on how the application processes keyboard messages. However, for "normal" applications, this will have the desired effect. Before the keystroke is played, the selected window will be brought to focus if the focus belongs to another window on the desktop. This function will normally always return code 0. This however does not necessarily mean that the keystroke had the desired effect. ═══ 6.31. LISTBOX_DESELECTITEM ═══ Deselect the specified item in a multiple selection listbox. Syntax rc = LISTBOX_DESELECTITEM(seq_number, item_number) where seq_number is the sequence number identifying the listbox, and item_number identifies the item. Examples /* Deselect the third item of the second multiple selection listbox */ rc = LISTBOX_DESELECTITEM("2", "3") Description This call deselects the specified item of the listbox. Notes This function works only on multiple selection listboxes. Return Codes o 5 - The listbox is not multiple selection o 6 - Deselection failed. Probably item number is out of range ═══ 6.32. LISTBOX_SELECTITEM ═══ Select the specified item in a listbox. Syntax rc = LISTBOX_SELECTITEM(seq_number, item_number) where seq_number is the sequence number identifying the listbox, and item_number identifies the item. Examples /* Select the third item of the second listbox */ rc = LISTBOX_DESELECTITEM("2", "3") Description This call selects the specified item of the listbox. Return Codes o 6 - Selection failed. Probably item number is out of range ═══ 6.33. LISTBOX_QUERY_COUNT ═══ Return the number of items in a listbox Syntax rc = LISTBOX_QUERY_COUNT(seq_number, variable_name) where seq_number is the sequence number identifying the listbox. Examples /* How many items in the first listbox? */ rc = LISTBOX_QUERY_COUNT("1", "item_count") say item_count 'items in the listbox' Description This call returns the number of items in the listbox. ═══ 6.34. LISTBOX_QUERY_TEXT ═══ Returns the text of the specified item in the specified listbox. Syntax rc = LISTBOX_QUERY_TEXT(seq_number, item_number, variable_name) where seq_number is the sequence number identifying the listbox, and item_number identifies the item. Examples /* Get the text of the third item in the second listbox */ rc = LISTBOX_QUERY_TEXT("2", "3", "txt") say "Text is " txt Description This function returns the text of the specified item in the specified listbox. Notes The text of the listbox cannot be queried if the listbox has the OWNERDRAW style. Return Codes o 5 - Query failed. Listbox has OWNERDRAW style o 6 - Query failed. Probably item number is out of range ═══ 6.35. MENU_QUERY_ALL ═══ Returns the text of each menu item at the specified level. Syntax rc = MENU_QUERY_ALL([level1,] [level2,] [level3,] stem_variable_name) where levels specify the point in the menu hierarchy to list items from. Examples /* Get contents of the action bar */ rc = MENU_QUERY_ALL("actionbar") if rc = 0 then do i = 1 to actionbar.0 say 'Action bar item 'i 'is :'actionbar.i end /* Get the contents of the Options action bar item */ rc = MENU_QUERY_ALL("~Options","options") if rc = 0 then do i = 1 to options.0 say 'Option choice 'i 'is :'options.i end Description This function returns the text of each of the action bar items at the specified level starting from the actionbar. Notes The returned text for any menu item may contain mnemonic indicators. For example in the above example, the tilde will appear in front of the mnemonic character O in Options. On input, however, the tilde is not necessary, and only the prefix of the menuitem text needs to be specified. ═══ 6.36. MENU_QUERY_STATE ═══ Returns information about the state of a menu item at the specified level. Syntax rc = MENU_QUERY_STATE([level1,] [level2,] [level3,] stem_variable_name) where levels specify the point in the menu hierarchy. Examples /* Get state of a menuitem in the action bar */ rc = MENU_QUERY_STATE("Options","Centered","state") if rc = 0 then do say 'Check status: 'state.1 say 'Hilite status: 'state.2 say 'Enable status: 'state.3 end Description This function returns updates the stem variable specified in the last parameter as follows. The .1 stem indicates the checked status and is either "CHECKED" or "UNCHECKED". The .2 stem indicates the hilite status and is either "HILITED" or "UNHILITED". The .3 stem indicates the enabled status and is either "ENABLED" or "DISABLED" ═══ 6.37. MENU_SELECT ═══ Selects the specified menu item. Syntax rc = MENU_SELECT( level1 [,level2] [,level3] ) where levels identify the menu item to be selected. Examples /* Select the File option of the action bar */ rc = MENU_SELECT("F~ile") rc = MENU_SELECT("File") /* This will match also */ rc = MENU_SELECT("Fi") /* This too */ /* Select the "Grid" choice of the "Options" action bar item */ rc = MENU_SELECT("~Options","G~rid") rc = MENU_SELECT("Options","Grid")/* This will match also */ Description This function selects the specified item at the level specified starting from the actionbar. Notes The matching is case sensitive, but the tilde is not needed, and only the prefix needs to be specified. Return Codes o 5 - Select failed. Menu item disabled or invalid. ═══ 6.38. MLE_HOW_MANY ═══ Return the number of multi line entryfields. Syntax rc = MLE_HOW_MANY( variable_name) Examples /* How many MLE controls */ rc = MLE_HOW_MANY( "count") say "number of MLE controls: " count Description This function returns a variable which contains the number of MLE controls in the selected window. ═══ 6.39. MLE_QUERY_TEXT ═══ Query the text of an multi line entry field. Syntax rc = MLE_QUERY_TEXT(seq_number, variable_name) where seq_number is the sequence number identifying the MLE. Examples /* Look at the items of the second MLE */ rc = MLE_QUERY_TEXT("2", "txt") say txt Description This function returns a variable which contains the text of the specified MLE. ═══ 6.40. MLE_SET_TEXT ═══ Set the text of an MLE field. Syntax rc = MLE_SET_TEXT( seq_number, text) where seq_number is the sequence number identifying the MLE field, and text is what it should be set to. Examples /* Set the value of the first MLE field */ rc = MLE_SET_TEXT("1", "MYAPP.EXE") Description This function sets the MLE field as specified. APMT will not update the field if it is not input enabled. Return Codes o 6 - The field is not enabled. ═══ 6.41. NOTEBOOK_PUSHTAB ═══ Simulates clicking on the specified tab of a notebook. Syntax rc = NOTEBOOK_PUSHTAB(seq_number, id) where seq_number is the sequence number of the notebook, and id is the text of the tab. Examples /* Select the 'Fonts' page */ rc = NOTEBOOK_PUSHTAB("1", "Fonts") Description This function simulates the clicking on the tab of a notebook page. Return Codes o 5 - Tab not found. ═══ 6.42. NOTEBOOK_QUERY_TABS ═══ Return the information on all tabs of a notebook. Syntax rc = NOTEBOOK_QUERY_TABS(seq_number,stem_variable_name) Examples /* List all the tabs of the notebook */ rc = NOTEBOOK_QUERY_TABS(1,"tabs") say "number of tabs: " tabs.0 do i = 1 to tabs.0 say 'Tab 'i 'is: 'tabs.i end Description This function returns a stem variable which contains the information on all the tabs of the specified notebook. The .0 stem contains the number of tabs, and the .i stem contains the text of the i-th tab. ═══ 6.43. POPUPMENU_QUERY_ALL ═══ Returns the text of each popup menu item at the specified level. Syntax rc = POPUPMENU_QUERY_ALL([level1,] [level2,] [level3,] stem_variable_name) where levels specify the point in the menu hierarchy to list items from. Description This function is similar to MENU_QUERY_ALL, except that it operates on a open popup menu. It is assumed that action such SHIFT+F10 has been issued to cause a popup prior to invoking this call. ═══ 6.44. POPUPMENU_QUERY_STATE ═══ Returns information about the state of a POPUPMENU item at the specified level. Syntax rc = POPUPMENU_QUERY_ALL([level1,] [level2,] [level3,] stem_variable_name) where levels specify the point in the POPUPMENU hierarchy. Description This function is similar to MENU_QUERY_STATE, except that it operates on a open popup menu. It is assumed that action such SHIFT+F10 has been issued to cause a popup prior to invoking this call. ═══ 6.45. POPUPMENU_SELECT ═══ Selects the specified menu item from the popup menu window. Syntax rc = POPUPMENU_SELECT( level1 [,level2] [,level3] ) where levels identify the menu item to be selected. Description This function is similar to MENU_SELECT, except that it operates on a open popup menu. It is assumed that action such SHIFT+F10 has been issued to cause a popup prior to invoking this call. Return Codes o 5 - Popup menu item disabled or invalid. ═══ 6.46. PUSHBUTTON_CLICK ═══ Simulates clicking of the specified pushbutton. Syntax rc = PUSHBUTTON_CLICK(id[,seq_number]) where id is the text of the checkbox, and seq_number is the sequence number of the pushbutton with the same id. Examples /* Click on Search */ rc = PUSHBUTTON_CLICK("Search") /* Click on the third pushbutton with a blank title */ rc = PUSHBUTTON_CLICK(" ",3) Description This function simulates the clicking on a pushbutton. The seq_number is optional and defaults to 1. It is needed only if the window has more than one pushbutton with the same id. ═══ 6.47. PUSHBUTTON_QUERY_ALL ═══ Return the information on all pushbuttons. Syntax rc = PUSHBUTTON_QUERY_ALL(stem_variable_name) Examples /* How many pushbuttons? */ rc = PUSHBUTTON_QUERY_ALL( "buttons") say "number of pushbuttons: " buttons.0 do i = 1 to buttons.0 say 'button no 'i 'is: 'buttons.i end Description This function returns a stem variable which contains the information on all the pushbuttons in the selected window. The .0 stem contains the number of pushbuttons, and the .i stem contains the text of the i-th pushbutton. If the pushbutton is disabled, then the text is followed by the word '*DISABLED*'. ═══ 6.48. RADIOBUTTON_CLICK ═══ This function causes the specified radiobutton to be clicked. Syntax rc = RADIOBUTTON_CLICK(id[,seq_number]) where id is the text identifying the radiobutton, and seq_number is the sequence number of the radiobutton with the id. Examples /* Click on the radiobutton "red" */ rc = RADIOBUTTON_CLICK("red") /* There are three "red" radiobuttons, press the third one */ rc = RADIOBUTTON_CLICK("red",3) Description As a result of this call the specified radiobutton will go to selected state. The second parameter is optional and defaults to 1. It is needed only if the window has more than one radiobutton with the same id. ═══ 6.49. RADIOBUTTON_QUERY_ALL ═══ Return the information on all radiobuttons. Syntax rc = RADIOBUTTON_QUERY_ALL(stem_variable_name) Examples /* How many radiobuttons? */ rc = RADIOBUTTON_QUERY_ALL( "button") say "number of radiobuttons: " button.0 do i = 1 to buttons.0 say 'radiobutton no 'i 'is: 'button.i end Description This function returns a stem variable which contains the information on all the radiobuttons in the selected window. ═══ 6.50. RADIOBUTTON_QUERY_STATE ═══ Return the checked, and enable status of the specified radiobutton. Syntax rc = RADIOBUTTON_QUERY_STATE(id, stem_variable_name) where id is the text identifying the radiobutton. Examples /* return the status of the "D" radiobutton */ rc = RADIOBUTTON_QUERY_STATE("D","state") if state.1 = 'CHECKED' then say 'selected' if state.2 = 'DISABLED' then say 'disabled' Description This function returns updates the stem variable specified in the last parameter as follows. The .1 stem indicates the checked status and is either "CHECKED" or "UNCHECKED". The .2 stem indicates the enabled status and is either "ENABLED" or "DISABLED". ═══ 6.51. SPINBUTTON_QUERY_LIMITS ═══ Return the limits of the values of the specified spinbutton. Syntax rc = SPINBUTTON_QUERY_LIMITS(seq_number, stem_variable_name) where seq_number is the sequence number identifying the SPINBUTTON. Examples /* return the limits of the second spinbutton */ rc = SPINBUTTON_QUERY_LIMITS("2","limits") say 'Lower limit is 'limits.1 say 'Upper limit is 'limits.2 Description This function returns the limits of the specified spinbutton. Stem .0 of the returned variable is always 2. Stems .1 and .2 contain the lower and upper limits. Notes This function does not seem to work under OS/2 2.0, possibly because of a PM bug. It has been found to work under OS/2 2.1. Return Codes o 5 - Limits could not be determined. Spinbutton is probably disabled. ═══ 6.52. SPINBUTTON_QUERY_TEXT ═══ Query the text of a spinbutton control. Syntax rc = SPINBUTTON_QUERY_TEXT(seq_number, variable_name) where seq_number is the sequence number identifying the SPINBUTTON. Examples /* Look at the text of the first SPINBUTTON */ rc = SPINBUTTON_QUERY_TEXT("1", "txt") say txt Description This function returns a variable which contains the text of the specified SPINBUTTON. ═══ 6.53. SPINBUTTON_SPINDOWN ═══ Spin the spinbutton down one unit. Syntax rc = SPINBUTTON_SPINDOWN(seq_number) where seq_number is the sequence number identifying the SPINBUTTON. Examples /* Spin down the first spinbutton, and look at the text */ rc = SPINBUTTON_SPINDOWN("1") rc = SPINBUTTON_QUERY_TEXT("1", "txt") say txt Description This function causes the down button to be clicked. Return Codes o 5 - Function did not be work. Spinbutton is probably disabled. ═══ 6.54. SPINBUTTON_SPINUP ═══ Spin the spinbutton up one unit. Syntax rc = SPINBUTTON_SPINUP(seq_number) where seq_number is the sequence number identifying the SPINBUTTON. Examples /* Spin up the first spinbutton, and look at the text */ rc = SPINBUTTON_SPINUP("1") rc = SPINBUTTON_QUERY_TEXT("1", "txt") say txt Description This function causes the up button to be clicked. Return Codes o 5 - Function did not be work. Spinbutton is probably disabled. ═══ 6.55. SYSMENU_QUERY_ALL ═══ Returns the text of each sysmenu item at the specified level. Syntax rc = SYSMENU_QUERY_ALL([level1,] [level2,] [level3,] stem_variable_name) where levels specify the point in the menu hierarchy to list items from. Description This function is similar to MENU_QUERY_ALL, except that it operates on a sysmenu. ═══ 6.56. SYSMENU_QUERY_STATE ═══ Returns information about the state of a sysmenu item at the specified level. Syntax rc = SYSMENU_QUERY_ALL([level1,] [level2,] [level3,] stem_variable_name) where levels specify the point in the sysmenu hierarchy. Description This function is similar to MENU_QUERY_STATE, except that it operates on a sysmenu. ═══ 6.57. SYSMENU_SELECT ═══ Selects the specified menu item from the sysmenu window. Syntax rc = SYSMENU_SELECT( level1 [,level2] [,level3] ) where levels identify the menu item to be selected. Description This function is similar to MENU_SELECT, except that it operates on the sysmenu. Return Codes o 5 - Sysmenu item disabled or invalid. ═══ 6.58. TEXT_HOW_MANY ═══ Return the number of static text fields. Syntax rc = TEXT_HOW_MANY( variable_name) Examples /* How many static text fields? */ rc = TEXT_HOW_MANY( "count") say "number of static text fields: " count Description This function returns a variable which contains the number of static text controls in the selected window. ═══ 6.59. TEXT_QUERY_TEXT ═══ Query the text of a static text field. Syntax rc = TEXT_QUERY_TEXT(seq_number, variable_name) where seq_number is the sequence number identifying the static field. Examples /* Get the text of the first static text field */ rc = TEXT_QUERY_TEXT("1", "txt") say txt Description This function returns a variable which contains the text of the specified static text field. ═══ 7. The APMT Mouse Functions ═══ This section describes those functions that are related mouse event simulation. This is at current experimental, and subject to change. Mouse event simulation is done by first using a function to place the pointer at the desired location, and then calling a function to play a mouse button event. For example, to simulate a drag and drop, at least four calls will be needed - set_pointer, button_down, set_pointer, and button up. ═══ 7.1. MOUSE ═══ Simulate a mouse action at the current pointer position. Syntax rc = MOUSE(action, button_identifier) where action defines the mouse action to be simulated, and button_identifier defines which button(s) to use. Examples rc = MOUSE("CLICK","1") /* click button 1 */ rc = MOUSE("DCLICK","2") /* double click button 2 */ rc = MOUSE("CLICK","12") /* click button 1,2 together*/ rc = MOUSE("CLICK","21") /* click button 1,2 together*/ Description This function will simulate the mouse event, without changing the pointer position. The following actions are supported. These are to be specified exactly. o CLICK o DCLICK o DOWN o UP Notes It is expected that the pointer is placed correctly, before invoking this function. Some functions to set the pointer are described in this section. Return Codes o 5 - Invalid parameter ═══ 7.2. FRAME_SET_POINTER ═══ Position the pointer at a specified position relative to the frame. Syntax rc = FRAME_SET_POINTER(location [,x][,X][,y][,Y]) where location the area of the frame, and the remaining parameters define the position within the frame, in the case when location is inside. Examples rc = FRAME_SET_POINTER("TR") /* place on upper right corner */ rc = FRAME_SET_POINTER("T") /* place on upper side */ rc = FRAME_SET_POINTER("BL") /* place on lower left corner */ rc = FRAME_SET_POINTER("M") /* at midpoint inside the frame */ rc = FRAME_SET_POINTER("M",4,10,6,10) /* at pos 4,6 on a 10x10 grid */ rc = FRAME_SET_POINTER("M",4,10) /* at pos 4,1 on a 10x1 grid */ Description This function will place the pointer at the specified location. If the first parameter is "M" then additional parameters may be specified as described below, otherwise the additional parameters will be ignored. The following values of the first parameter are supported: "R" - RIGHT side of the frame "W" - LEFT side of the frame "T" - TOP side of the frame "B" - BOTTOM side of the frame "TR" - TOP RIGHT corner of the frame "TL" - TOP LEFT corner of the frame "BL" - BOTTOM LEFT corner of the frame "BR" - BOTTOM RIGHT corner of the frame "M" - Inside the frame The 2nd through 5th parameters, if specified, are used as follows. The frame window is divided into an imaginary grid of dimension (X,Y). The pointer is then placed in the middle of the (x,y) cell of this grid. Notes If the first parameter is not recognized, "M" is assumed. ═══ 7.3. CLASS_SET_POINTER ═══ Position the pointer at a specified position in a user defined control. Syntax rc = CLASS_SET_POINTER(classname [,x][,X][,y][,Y]) where classname is the name of a user defined control, and the remaining defines the position within the window. Examples rc = CLASS_SET_POINTER("MyButton") /* place at center of user defined */ /* button (of class MyButton) */ Description This function will place the pointer at the specified location, inside the control window belonging to the class specified in the first parameter. The matching is exact. The 2nd through 5th parameters, if specified, are used as follows. The specified control window is divided into an imaginary grid of dimension (X,Y). The pointer is then placed in the middle of the (x,y) cell of this grid. Notes To be able to use this function, you have to know the name of the user defined (non-standard) control. You can use the PMTREE tool to determine this. ═══ 7.4. CONTAINER_SET_POINTER ═══ Place the pointer on the specified container object. Syntax rc = CONTAINER_SET_POINTER(seq_number, object_identifier) where seq_number is the sequence number identifying the container, and object_identifier is either the text string identifying the object or #n with n = the object number Examples rc = CONTAINER_SET_POINTER(1, "Main") rc = CONTAINER_SET_POINTER(1, "#5") Description This function will first try to place the pointer on the icon rectangle of the selected container object. If unsuccessful, attempt will be made to place the pointer on the text rectangle. Notes If the object is not visible, attempt will be made to bring the object into the visible area by scrolling the container. Return Codes o 5 - No matching object found. o 6 - Could not get rectangle of icon or text.